/****************************************************************************
 * Copyright 2009 Huascar A. Sanchez                                        *
 *                                                                          *
 * Licensed under the Apache License, Version 2.0 (the "License");          *
 * you may not use this file except in compliance with the License.         *
 * You may obtain a copy of the License at                                  *
 *                                                                          *
 *     http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                          *
 * Unless required by applicable law or agreed to in writing, software      *
 * distributed under the License is distributed on an "AS IS" BASIS,        *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 * See the License for the specific language governing permissions and      *
 * limitations under the License.                                           *
 ****************************************************************************/
package com.gotobject.filter.common;

import com.gotobject.filter.ordering.AbstractOrdering;
import com.gotobject.filter.ordering.Ordering;
import static com.gotobject.filter.internal.Objects.asIterable;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public class Orderings {
    private Orderings(){}

    public static <T> Ordering<T> desc(){
        final class Reverser{
            <T> void reverse(T[] b){
                int left  = 0;          // index of leftmost element
                int right = b.length-1; // index of rightmost element
                while (left < right) {
                   // exchange the left and right elements
                   final T temp = b[left];
                   b[left]      = b[right];
                   b[right]     = temp;
                   // move the bounds toward the center
                   left++;
                   right--;
                }
            }
        }
        return new AbstractOrdering<T>(){
            @SuppressWarnings({"unchecked"}) // they should be of the same type.
            @Override
            public Iterable<T> of(Iterable<? super T> source) {
                final List<T>       asList          = new ArrayList<T>();
                final Iterable<T>   asCastSource    = asIterable(source);
                for(T each : asCastSource){
                    asList.add(each);
                }
                final T[] a = asList.toArray((T[]) Array.newInstance(asList.get(0).getClass(), asList.size()));
                new Reverser().reverse(a);
                return Arrays.asList(a);
            }

            @Override
            public String toString() {
                return "orderBy(desc)";
            }
        };
    }

    public static <T> Ordering<T> multipleOf(final int number){
        return new AbstractOrdering<T>() {
            @Override
            public Iterable<T> of(Iterable<? super T> source) {
                final List<T>       multipleOnes    = new ArrayList<T>();
                final List<T>       nonMultipleOnes = new ArrayList<T>();
                final Iterable<T>   asCastSource    = asIterable(source);
                for(T each : asCastSource){
                    if(!(each instanceof Integer)) throw new IllegalArgumentException("wrong type: only integers are accepted.");
                    if((Integer.class.cast(each) % number)==0){
                        multipleOnes.add(each);
                    } else {
                        nonMultipleOnes.add(each);
                    }
                }
                multipleOnes.addAll(nonMultipleOnes);
                return multipleOnes;
            }

            @Override
            public String toString() {
                return "multipleOf(" + number + ")";
            }
        };
    }

    public static <T> Ordering<T> length(){
        return new AbstractOrdering<T>(){
            @Override
            public Iterable<T> of(Iterable<? super T> source) {
                final List<T>       asList          = new ArrayList<T>();
                final Iterable<T>   asCastSource    = asIterable(source);
                for(T each : asCastSource){
                    asList.add(each);
                }

                @SuppressWarnings({"unchecked"}) // they should be of the same type.
                final T[] a = asList.toArray((T[]) Array.newInstance(
                        asList.get(0).getClass(),
                        asList.size()
                ));
                
                Arrays.sort(a, new Comparator<T>(){
                    @Override
                    public int compare(T t1, T t2) {
                        if (t1.toString().length() < t2.toString().length()) {
                          return -1;
                        } else if (t1.toString().length() > t2.toString().length()) {
                          return 1;
                        } else {
                          return 0;
                        }
                    }
                });
                return Arrays.asList(a);
            }

            @Override
            public String toString() {
                return "orderby(length)";
            }
        };
    }
}
